//
//  Model3D_obj_Exporter.cpp
//  C3W_Demo_By_Fan
//
//  Created by 范静涛 on 2024/7/21.
//

//------------------------------------------------------------------------------
//【文件名】Model3D_obj_Exporter.cpp
//【功能模块和目的】导出Model3D到obj文件的导出器类实现
//【开发者及日期】范静涛 2024/7/21
//【更改记录】
//------------------------------------------------------------------------------

//自身类头文件
#include "Model3D_obj_Exporter.hpp"
//Exporter基类模版所属头文件
#include "Exporter.hpp"
//Model3D类所属头文件
#include "Model3D.hpp"
//size_t所属头文件
#include <cstddef>
//Point3D类所属头文件
#include "Point3D.hpp"
//Face3D类所属头文件
#include "Face3D.hpp"
//Line3D类所属头文件
#include "Line3D.hpp"
//std::ofstream所属头文件
#include <fstream>
//StringList所属头文件
#include "StringList.hpp"
//Point3DSet所属头文件
#include "Point3DSet.hpp"
//std::vector所属头文件
#include <vector>

//--------------------------------------------------------------------------
//静态数据成员
//--------------------------------------------------------------------------
//是否压缩导出，默认为压缩导出
bool Model3D_obj_Exporter::IsCompressedPoints{true};

//------------------------------------------------------------------------------
//必要的构造、析构、赋值行为
//------------------------------------------------------------------------------

//函数名：Model3D_obj_Exporter
//功能：默认构造函数，手动触发基类构造函数，指定文件扩展名为"obj"
//入口参数：无
//出口参数：无
//返回值：无
Model3D_obj_Exporter::Model3D_obj_Exporter()
: Exporter<Model3D>(std::string{"obj"}){
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//受保护的 非静态Getter成员函数：需检查所有Getter是否有后置const
//------------------------------------------------------------------------------

//函数名：SaveToStream const
//功能：将Modle3D类型对象导出到文件流的接口override
//入口参数：std::ofstream& Stream, const Model3D& AModel
//出口参数：std::ofstream& Stream
//返回值：无
void Model3D_obj_Exporter::SaveToStream
(std::ofstream& Stream, const Model3D& AModel) const{
    //保存模型注释
    for (size_t i = 0; i < AModel.Comments.Count; i++) {
        Stream << "# " << AModel.Comments[i] << std::endl;
    }
    
    //保存模型名
    Stream << "g " << AModel.Name << std::endl;
    
    //如果是压缩存储
    if (IsCompressedPoints) {
        SaveToStream_Compress(Stream, AModel);
    }
    //非压缩存储
    else {
        //保存所有线上的点
        for (auto pLine : AModel.Lines)  {
            Stream << "v " << pLine->Points[0] << std::endl;
            Stream << "v " << pLine->Points[2] << std::endl;
        }

        //保存所有面上的点
        for (auto pFace : AModel.Faces)  {
            Stream << "v " << pFace->Points[0] << std::endl;
            Stream << "v " << pFace->Points[2] << std::endl;
            Stream << "v " << pFace->Points[3] << std::endl;
        }
        
        //保存线
        size_t Index = 0;
        for (auto pLine : AModel.Lines) {
            Stream << "l " << Index << ' ' << (Index + 1) << std::endl;
            Index += 2;
        }
        
        //保存面
        for (auto pFace : AModel.Faces) {
            Stream << "f " << Index << ' '
                           << (Index + 1) << ' '
                           << (Index + 2) << std::endl;
            Index += 3;
        }
    }
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//受保护的 非静态Getter成员函数：需检查所有Getter是否有后置const
//------------------------------------------------------------------------------

//函数名：SaveToStream const
//功能：将Modle3D类型对象"压缩"导出到文件流的接口
//入口参数：std::ofstream& Stream, const Model3D& AModel
//出口参数：std::ofstream& Stream
//返回值：无
void Model3D_obj_Exporter::SaveToStream_Compress
(std::ofstream& Stream, const Model3D& AModel) const{
    //非重复点集
    Point3DSet TempPoints;
    
    //添加所有Face3D中的点
    for (auto pFace : AModel.Faces) {
        for (size_t i = 0; i < 3; i++){
            //捕获，但不处理异常
            try {
                TempPoints.Append(pFace->Points[i]);
            } catch (...) {
                //碰到重复点，什么都不做
            }
        }
    }
    
    //添加所有Line3D中的点
    for (auto pLine : AModel.Lines) {
        for (size_t i = 0; i < 2; i++){
            //捕获，但不处理异常
            try {
                TempPoints.Append(pLine->Points[i]);
            } catch (...) {
                //碰到重复点，什么都不做
            }
        }
    }
    
    //保存所有点
    std::vector<Point3D> PointsToSave;
    for (size_t i = 0; i < TempPoints.Count; i++) {
        Stream << "v " << TempPoints[i] << std::endl;
        PointsToSave.push_back(TempPoints[i]);
    }
    
    //保存线
    for (auto pLine : AModel.Lines) {
        Stream << "l ";
        for (size_t i = 0; i < 2; i++) {
            auto ItCurrent = std::find(
                PointsToSave.begin(),
                PointsToSave.end(),
                pLine->Points[i]);
            Stream << ItCurrent - PointsToSave.begin() + 1 << ' ';
        }
        Stream << std::endl;
    }
    
    //保存面
    for (auto pFace : AModel.Faces) {
        Stream << "f ";
        for (size_t i = 0; i < 3; i++) {
            auto ItCurrent = std::find(
                PointsToSave.begin(),
                PointsToSave.end(),
                pFace->Points[i]);
            Stream << ItCurrent - PointsToSave.begin() + 1 << ' ';
        }
        Stream << std::endl;
    }
}
//------------------------------------------------------------------------------
